package org.lep.leetcode.wordladder;

import java.util.*;

/**
 * Source : https://oj.leetcode.com/problems/word-ladder/
 *
 * Created by lverpeng on 2017/8/23.
 *
 * Given two words (start and end), and a dictionary, find the length of shortest
 * transformation sequence from start to end, such that:
 *
 * Only one letter can be changed at a time
 * Each intermediate word must exist in the dictionary
 *
 * For example,
 *
 * Given:
 * start = "hit"
 * end = "cog"
 * dict = ["hot","dot","dog","lot","log"]
 *
 * As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
 * return its length 5.
 *
 * Note:
 *
 * Return 0 if there is no such transformation sequence.
 * All words have the same length.
 * All words contain only lowercase alphabetic characters.
 *
 */
public class WordLadder {

    /**
     *
     * 转化为图的问题
     * start、end、dict中各个单词看做是图中的每个节点
     * 如果有一个单词能变化一个字母到另外一个单词，说明两个节点是连通的
     *
     * 所以就转化为求两个节点之间的最短距离，使用BFS
     *
     * 使用BFS注意：
     * 1. 找到当前需要遍历的节点，这里是当前节点的相邻节点
     * 2. 标记已经遍历过的节点，防止从重复遍历
     *
     * 从start开始使用BFS，遍历当前节点的相邻节点，求出当前节点的相邻节点两种办法：
     * 1. 遍历字典中每个单词，如果和当前单词只差一个字母，说明是相邻的，复杂度为：w*n，w是当前单词的长度，n是字典单词数量
     * 2. 针对当前单词的每个字母，找出可能得变化，每个字母可以变为26个字母中除本身外的其他字母,如果判断变化后的单词在字典中则为相邻的节点，
     *          复杂度为 26*w，w为单词长度
     *
     * 当字典中单词数量较小的时候可以使用第一种方法，如果字典中单词数量较大则使用第二种方法
     *
     * 怎么标记访问过的节点？
     * 已经访问过的节点不需要再次被访问，所以可以从字典中删除
     *
     * 这里使用第二种方法
     *
     * @param start
     * @param end
     * @param dict
     * @return
     */
    public int ladderLength (String start, String end, String[] dict) {
        Set<String> set = new HashSet<String>(Arrays.asList(dict));
        set.add(end);
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put(start, 1);
        while (map.size() > 0) {
            String cur = map.keySet().iterator().next();
            Integer len = map.get(cur);
            if (cur.equals(end)) {
                System.out.println(len);
//                return len;
            }
            map.remove(cur);
            Set<String> neighbors = findNeighbors(cur, set);
            for (String str : neighbors) {
                map.put(str, len+1);
            }
        }

        return 0;

    }

    private Set<String> findNeighbors (String cur, Set<String> dict) {
        Set<String> neighbors = new HashSet<String>();
        for (int i = 0; i < cur.length(); i++) {
            for (int j = 0; j < 26; j++) {
                char ch = (char) ('a' + j);
                if (cur.charAt(i) != ch) {
                    String candidate = "";
                    if (i == cur.length()-1) {
                        candidate = cur.substring(0, i) + ch;
                    } else {
                        candidate = cur.substring(0, i) + ch + cur.substring(i+1);
                    }
                    if (dict.contains(candidate)) {
                        neighbors.add(candidate);
                        dict.remove(candidate);
                    }
                }
            }
        }
        return neighbors;
    }

    public static void main(String[] args) {
        WordLadder wordLadder = new WordLadder();
        String start = "hit";
        String end = "cog";
        String[] dict = new String[]{"hot","dot","dog","lot","log"};
        System.out.println(wordLadder.ladderLength(start, end, dict) + "----5");

    }
}
